<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>创造银河</title>
<meta name="description" content="">
<meta name="keywords" content="">


<style>
body {
	margin:0;
}
canvas {
	position:absolute;
	top:0;
	left:0;
	width:100%;
	height:100%;
}
</style>

</head>
<body>

<script type="text/javascript">
    var w = window.innerWidth;
	var h = window.innerHeight;

	var dustCanvas = document.createElement('canvas');
	var dustCtx = dustCanvas.getContext('2d');
	var starCanvas = document.createElement('canvas');
	var starCtx = starCanvas.getContext('2d');

	document.body.appendChild(dustCanvas);
	document.body.appendChild(starCanvas);

	dustCanvas.width = starCanvas.width = w;
	dustCanvas.height = starCanvas.height = h;

	dustCtx.globalCompositeOperation = 'lighter';
	starCtx.globalCompositeOperation = 'lighter';

	var galaxies = [];

	var mouse = {
	  pos: {
		x: w * 0.5,
		y: h * 0.5
	  },
	  speed: 0
	};

	var randomNumbers = length => Array.from(new Array(length), () => Math.random());
	var PI = Math.PI;
	var TAU = PI * 2;
	var r = () => Math.random();
	var angle2 = (p1,p2) => Math.atan2(
	  p2[1]-p1[1],
	  p2[0]-p1[0]
	);
	var distance2 = (p1,p2) => Math.sqrt(
	  Math.pow(p1[0]-p2[0], 2) + 
	  Math.pow(p1[1]-p2[1], 2)
	);

	var createDustParticle = (color) => {
	  
	  var canvas = document.createElement('canvas');
	  
	  var w = 100;
	  var h = 100;
	  var cx = w * 0.5;
	  var cy = h * 0.5;
	  
	  canvas.width = w;
	  canvas.height = h;
	  var ctx = canvas.getContext('2d');
	  canvas.ctx = ctx;
	  
	  var xRand = -5 + (r()*10);
	  var yRand = -5 + (r()*10);
	  var xRand2 = 10 + (r()*(cx/2));
	  var yRand2 = 10 + (r()*(cy/2));
	  
	  var color = color || {
		r: Math.round(150+(r()*100)),
		g: Math.round(50+(r()*100)),
		b: Math.round(50+(r()*100))
	  };
	  
	  ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},.02)`;
	  
	  Array
		.from(new Array(200), () => randomNumbers(3))
		.forEach( (p,i,arr) => {
		  var length = arr.length;
		  
		  var x = Math.cos( TAU/xRand/length*i ) * p[2]*xRand2 + cx;
		  var y = Math.sin( TAU/yRand/length*i ) * p[2]*yRand2 + cy;
		  
		  ctx.beginPath();
		  ctx.moveTo(x, y);
		  ctx.arc(x, y, p[2]*4, 0, TAU);
		  ctx.fill();
		
		});
	  
		return canvas;
	};

	var Galaxy = function(x,y) {
	  
	  var g = this;
	  
	  g.x = x;
	  g.y = y;
	  g.stars = [];
	  g.dust = [];
	  g.drag = r();
	  
	  g.angleOffsetX = TAU*r();
	  g.angleOffsetY = TAU*r();
	  g.realAngleOffsetX = 0;
	  g.realAngleOffsetY = 0;
	  
	  g.color = {
		r: Math.round(50+(r()*100)),
		g: Math.round(50+(r()*100)),
		b: Math.round(150+(r()*100))
	  };
	  
	  var calculateStarDustParams = o => {
		o.angle = angle2([g.x,g.y], [o.x,o.y]);
		o.distance = distance2([g.x,g.y], [o.x,o.y]);
		o.xAspect = [o.x/o.y];
		o.yAspect = [o.y/o.x];
	  };
	  
	  g.calculateCenter = () => {
		if (!g.stars.length) return;
		g.x = g.stars
		  .map(s => s.x)
		  .reduce((previous,current) => previous + current)
		  / g.stars.length;
		
		g.y = g.stars
		  .map(s => s.y)
		  .reduce((previous,current) => previous + current)
		  / g.stars.length;
		
		g.stars.forEach(calculateStarDustParams);
		g.dust.forEach(calculateStarDustParams);
	  };
	  
	};

	var Star = function(x, y, spread) {
	  var s = this;
	  s.x = x + Math.cos(TAU*r()) * spread;
	  s.y = y + Math.sin(TAU*r()) * spread;
	  s.radius = r()+0.25;
	  s.speed = r();
	};

	var Dust = function(x, y, size) {
	  var d = this;
	  d.x = x;
	  d.y = y;
	  d.size = size;
	  d.texture = createDustParticle();
	  d.speed = r();
	};

	var updateStarDust = (s,g) => {
	  if (g == currentGalaxy && drawingMode) return;
	  s.angle += (0.5+(s.speed*0.5))/s.distance;
	  s.x = g.x + (Math.cos(s.angle+g.realAngleOffsetX)*s.distance);
	  s.y = g.y + (Math.sin(s.angle+g.realAngleOffsetY)*s.distance);
	};

	var update = () => {
	  galaxies.forEach(g => {
		if (g != currentGalaxy) {
		  g.realAngleOffsetX +=
			g.realAngleOffsetX < g.angleOffsetX
			? (g.angleOffsetX-g.realAngleOffsetX)*0.05 : 0;
		  g.realAngleOffsetY +=
			g.realAngleOffsetY < g.angleOffsetY
			? (g.angleOffsetY-g.realAngleOffsetY)*0.05 : 0;
		}
		g.stars.forEach((s) => {
		  /*s.distance -= s.distance < 2
			? 0
			: TAU/s.distance;*/
		  updateStarDust(s,g);
		});
		g.dust.forEach((d) => {
		  /*d.distance -= d.distance < 50
			? 0
			: TAU/d.distance;*/
		  updateStarDust(d,g);
		});
	  });
	};

	var render = () => {
	  
	  dustCtx.globalCompositeOperation = 'source-over';
	  dustCtx.fillStyle = 'rgba(0,0,0,.05)';
	  dustCtx.fillRect(0,0,w,h);
	  dustCtx.globalCompositeOperation = 'lighter';

	  starCtx.clearRect(0,0,w,h);
	  starCtx.fillStyle = '#ffffff';
	  starCtx.strokeStyle = 'rgba(255,255,255,.05)';
	  starCtx.beginPath();
	  
	  if (drawingMode) galaxies.forEach(g => {
		starCtx.moveTo(g.x, g.y);
		starCtx.arc(g.x,g.y,2,0,TAU);
	  });
	  
	  galaxies.forEach(g => {
		g.stars.forEach(s => {
		  starCtx.moveTo(s.x, s.y);
		  starCtx.arc(s.x,s.y,s.radius,0,TAU);
		});
		g.dust.forEach(d => {
		  dustCtx.drawImage(d.texture, d.x-(d.size*0.5), d.y-(d.size*0.5), d.size, d.size)
		});
	  });
	  
	  dustCtx.fill();
	  starCtx.fill();
	  
	  if (drawingMode && currentGalaxy) {
		starCtx.beginPath();
		currentGalaxy.stars.forEach((s,i) => {
			starCtx.moveTo(s.x, s.y);
			starCtx.lineTo(currentGalaxy.x, currentGalaxy.y);
		});
		starCtx.stroke();
	  }
	  
	};

	var currentGalaxy = null;

	var drawingMode = false;

	var activateDraw = e => {
	  drawingMode = true;
	  mouse.pos.x = e.layerX;
	  mouse.pos.y = e.layerY;
	  currentGalaxy = new Galaxy(e.layerX, e.layerY);
	  galaxies.push(currentGalaxy);
	};
	var disableDraw = e => {
	  
	  drawingMode = false;
	  currentGalaxy = null;
	};
	var draw = e => {
	  if (!drawingMode) return;
	  
	  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));
	  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));
	  currentGalaxy.stars.push(new Star(mouse.pos.x, mouse.pos.y, mouse.speed));
	  
	  if (mouse.speed*1.5 > 13 && mouse.speed < 100) currentGalaxy.dust.push(
		new Dust(
		  (currentGalaxy.x + (Math.cos(TAU*r()) * mouse.speed*0.1)),
		  (currentGalaxy.y + (Math.sin(TAU*r()) * mouse.speed*0.1)),
		  mouse.speed*1.5
		)
	  );
	  
	  currentGalaxy.calculateCenter();
	  
	};

	var loop = () => {
	  draw();
	  update();
	  render();
	  window.requestAnimationFrame(loop);
	};

	loop();

	var moveEvent = e => {
	  mouse.speed =  distance2(
		[e.layerX,e.layerY],
		[mouse.pos.x,mouse.pos.y]
	  );
	  mouse.pos.x = e.layerX;
	  mouse.pos.y = e.layerY;
	  draw(e);
	};

	window.addEventListener('mousedown', activateDraw);
	window.addEventListener('mousemove', moveEvent);
	window.addEventListener('mouseup',disableDraw);


</script>

</body>
</html>